package com.oneplatform.permission.service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import com.mendmix.common.model.IdNamePair;
import com.mendmix.common.model.Page;
import com.mendmix.common.model.PageParams;
import com.mendmix.mybatis.plugin.pagination.PageExecutor;
import com.oneplatform.permission.constants.BindingRelationType;
import com.oneplatform.permission.constants.GrantRelationType;
import com.oneplatform.permission.constants.SubRelationType;
import com.oneplatform.permission.dao.mapper.ObjectRelationMapper;
import com.oneplatform.permission.dto.ObjectRelation;
import com.oneplatform.permission.dto.param.GrantRelationParam;

/**
 * 
 * <br>
 * Class Name   : RelationInternalService
 *
 * @author jiangwei
 * @version 1.0.0
 * @date 2019年12月23日
 */
@Service
public class InternalRelationService {

	private @Autowired ObjectRelationMapper objectRelationMapper;

	
	public void deleteFunctionResourceRelations(Integer id) {
        objectRelationMapper.deleteGrantRelation(new ObjectRelation(GrantRelationType.funcToRole.name(), id.toString(),null));
        objectRelationMapper.deleteSubordinateRelation(new ObjectRelation(SubRelationType.apiToFunc.name(), null, id.toString()));
	}
	
	public void deleteFunctionRoleRelations(Integer id) {
		ObjectRelation relation = new ObjectRelation();
		relation.setRelationType(SubRelationType.userToRole.name());
		relation.setSecondId(id.toString());
		objectRelationMapper.deleteSubordinateRelation(relation);
		//
		relation.setRelationType(GrantRelationType.apiToRole.name());
		relation.setSecondId(id.toString());
		objectRelationMapper.deleteGrantRelation(relation);
		//
		relation.setRelationType(GrantRelationType.funcToRole.name());
		relation.setSecondId(id.toString());
		objectRelationMapper.deleteGrantRelation(relation);
		//
		relation.setRelationType(BindingRelationType.roleToDept.name());
		relation.setFirstId(id.toString());
		relation.setSecondId(null);
		objectRelationMapper.deleteBindingRelation(relation);
		//
		relation.setRelationType(BindingRelationType.roleToPost.name());
		relation.setFirstId(id.toString());
		objectRelationMapper.deleteBindingRelation(relation);
	}
	
	/**
	 * 更新从属关系
	 * @param relationType
	 * @param parentId
	 * @param childIds
	 */
	@Transactional(rollbackFor = Exception.class)
	public void updateParentSubRelations(SubRelationType relationType,String parentId, List<String> childIds) {
		if (childIds == null) {
			childIds = new ArrayList<>(0);
		}
		ObjectRelation relation = new ObjectRelation(relationType.name(), parentId, null);
		Map<String, ObjectRelation> existRelations = objectRelationMapper.findSubordinateRelations(relation)
				                           .stream()
				                           .collect(Collectors.toMap(ObjectRelation::getSecondId, Function.identity()));

		List<String> addIdList;
		List<String> removeIdList = null;
		if (!existRelations.isEmpty()) {
			addIdList = new ArrayList<>(childIds);
			addIdList.removeAll(existRelations.keySet());
			removeIdList = new ArrayList<>(existRelations.keySet());
			removeIdList.removeAll(childIds);
		} else {
			addIdList = childIds;
		}
		// add new
		if (addIdList != null && !addIdList.isEmpty()) {
			List<ObjectRelation> addList = addIdList.stream().map(childId -> {
				return new ObjectRelation( relationType.name(), parentId, childId);
			}).collect(Collectors.toList());
			objectRelationMapper.insertSubordinateRelation(addList);
		}
		// remove his
		if (removeIdList != null && !removeIdList.isEmpty()) {
			for (String childId : removeIdList) {
				objectRelationMapper.deleteSubordinateRelation(new ObjectRelation( relationType.name(), parentId, childId));
			}
		}
	}

	@Transactional(rollbackFor = Exception.class)
	public void updateChildSubRelations(SubRelationType relationType,String childId, List<String> parentIds) {
		ObjectRelation relation = new ObjectRelation(relationType.name(), null, childId);
		Map<String, ObjectRelation> existRelations = objectRelationMapper.findSubordinateRelations(relation)
		                 .stream()
                         .collect(Collectors.toMap(ObjectRelation::getFirstId, Function.identity()));
		List<String> addIdList;
		List<String> removeIdList = null;
		if (!existRelations.isEmpty()) {
			addIdList = new ArrayList<>(parentIds);
			addIdList.removeAll(existRelations.keySet());
			removeIdList = new ArrayList<>(existRelations.keySet());
			removeIdList.removeAll(parentIds);
		} else {
			addIdList = parentIds;
		}
		// add new
		if (addIdList != null && !addIdList.isEmpty()) {
			List<ObjectRelation> addList = addIdList.stream().map(parentId -> {
				return new ObjectRelation( relationType.name(), parentId, childId);
			}).collect(Collectors.toList());
			objectRelationMapper.insertSubordinateRelation(addList);
		}
		// remove his
		if (removeIdList != null && !removeIdList.isEmpty()) {
			for (String parentId : removeIdList) {
				objectRelationMapper.deleteSubordinateRelation(new ObjectRelation(relationType.name(), parentId, childId));
			}
		}
	}

	/**
	 * 更新授权
	 * @param param 授权参数
	 */
	@Transactional(rollbackFor = Exception.class)
	public void updateGrantRelations(GrantRelationParam param) {
		List<String> sourceIdList = param.getSourceIdList();
		if(CollectionUtils.isEmpty(sourceIdList)){
			sourceIdList = new ArrayList<>(0);
		}
		ObjectRelation relation = new ObjectRelation(param.getRelationType().name(), null, param.getTargetId());
		Map<String, ObjectRelation> existRelations = objectRelationMapper.findGrantRelations(relation)
				.stream()
				.collect(Collectors.toMap(ObjectRelation::getFirstId,Function.identity()));
		List<String> addIdList;
		List<String> removeIdList = null;
		if(!existRelations.isEmpty()){
			addIdList = new ArrayList<>(sourceIdList);
			addIdList.removeAll(existRelations.keySet());
			removeIdList = new ArrayList<>(existRelations.keySet());
			removeIdList.removeAll(sourceIdList);
		}else {
			addIdList = sourceIdList;
		}
		//add new
		if(!CollectionUtils.isEmpty(addIdList)){
			List<ObjectRelation> addList = addIdList.stream().map(sourceId -> {
				return new ObjectRelation(param.getRelationType().name(), sourceId, param.getTargetId());
			}).collect(Collectors.toList());
			objectRelationMapper.insertGrantRelation(addList);
		}
		//remove his
		if(!CollectionUtils.isEmpty(removeIdList)){
			removeIdList.stream().forEach(sourceId -> {
				objectRelationMapper.deleteGrantRelation(new ObjectRelation(param.getRelationType().name(), sourceId, param.getTargetId()));
			});
		}
	}

	
	public Map<String, String> findGrantRelationIdMappings(GrantRelationType relationType,String targetId){
		ObjectRelation relation = new ObjectRelation(relationType.name(), null, targetId);
		List<ObjectRelation> relations = objectRelationMapper.findGrantRelations(relation);
		Map<String, String> map = new HashMap<>(relations.size());
		for (ObjectRelation entity : relations) {
			map.put(entity.getFirstId(), entity.getSecondId());
		}
		return map;
	}
	
	public List<IdNamePair> findSubRelationChildren(SubRelationType relationType,String parentId){
		return objectRelationMapper.findSubChildIdNamePairs(relationType.name(), parentId);
	}
	
	public Page<IdNamePair> pageSubRelationChildren(PageParams pageParam,SubRelationType relationType,String parentId){
		return PageExecutor.pagination(pageParam, () -> objectRelationMapper.findSubChildIdNamePairs(relationType.name(), parentId));
	}

}
